#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <signal.h>
#include <netdb.h>
#include <string>
#include <pthread.h>
#include <list>
#include <math.h>
#include "definitions.hpp"

using namespace std;

void *tcp_thread_server_func(void* d2){
    //Data* d = (Data*) d2; 
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    unsigned char buffer[8];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = d1->getPortNum();
    cout<<portno<<endl;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    if (bind(sockfd, (struct sockaddr *) &serv_addr,
             sizeof(serv_addr)) < 0) 
        error("ERROR on binding");
    listen(sockfd,5);
    clilen = sizeof(cli_addr);
    cout<<"waiting on connections"<<endl;
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    if (newsockfd < 0)
    {
        error("ERROR on accept");
    } 
	else {
        bzero(buffer,8);
        n = read(newsockfd,buffer,8);
        if (n < 0) error("ERROR reading from socket");
		memcpy(&fsize,buffer,8);
        num_packets = ceil(fsize/((double)max_load_size));
	    last_pack = fsize%max_load_size;
        // signal the udp thread here ....
        
        pthread_mutex_lock(&synch_lock);
        pthread_cond_broadcast(&wakeup_udp);
        pthread_mutex_unlock(&synch_lock);
        cout<<"sending reply"<<endl;
        n = write(newsockfd,"NACK",4);
        if (n < 0){ 
		    file_complete_flag =1;
            error("ERROR writing to socket");
            close(newsockfd);   
            close(sockfd);
        }
    }
    pthread_t udp_dup_thread;
    pthread_attr_t attr_udp_dup;
    int thrd_udp_dup=0;    
    int overhead=0;
    
    pthread_attr_init(&attr_udp_dup);
    pthread_attr_setdetachstate(&attr_udp_dup, PTHREAD_CREATE_JOINABLE);
    cout<<"Before pthread create udpSFASFDAFD"<<endl;
    thrd_udp_dup=pthread_create(&udp_dup_thread,&attr_udp_dup,udp_dup_func,(void *)overhead);
    if(thrd_udp_dup){
        cerr<<"Error; return code from thread tcp create call is"<<thrd_udp_dup<<endl;
        exit(-1);
    } 
    
    pthread_attr_destroy(&attr_udp_dup);
    
    pthread_mutex_lock(&sleep_lock);
	pthread_cond_wait(&sleep_cond,&sleep_lock);
    pthread_mutex_unlock(&sleep_lock);
    
    n = write(newsockfd,"0",1);
    if (n<0) {
        error("writetcp");
    }
    
    close(sockfd);
    close(newsockfd);
	void *status_udp;
	
	int thrd_udp=pthread_join(udp_dup_thread,&status_udp);
    if(thrd_udp){
        cerr<<"Error; return code from thread tcp join call is "<<thrd_udp<<endl;
        exit(-1);
    }
    cout<<"tcpthreadserver exiting"<<endl;
    
    pthread_exit(NULL);
}


